<?php
/**
 * XMLMenu-Klasse
 * Struktur wird anhand XML vorgenommen und aus XML-Struktur Menue generiert
 *
 * XML im Format:
 * <root>
 *	 <element id="INDEX_INDEX" text="Start" title="Zur Startseite" url="path1/path2" />
 *	 <element id="ACCOUNT_INDEX" text="Mein Konto" title="Kontodaten verwalten" url="account">
 *	 	<element id="ACCOUNT_SHOP" text="Meine PDFs" title="Meine PDFs anzeigen" url="account/pdf" />
 * 		<element id="ACCOUNT_SHOP_EDIT" text="PDFs bearbeiten" title="Meine PDFs bearbeiten" url="account/pdf/edit" />
 *	 </element>
 *	 <element id="BASKET" text="Warenkorb" title="Mein Warenkorb anzeigen" url="basket" />
 * </root>
 *
 * @author		Joachim Ruf
 * @copyright	Copyright (c) 2003-2010 Loresoft Software (http://www.loresoft.de)
 * @license		http://yapary.loresoft.de/lizenzbedingungen     New BSD License
 *
 * */



Class Yapary_Menu_XmlMenu
{
	private $id;
	private $basePath;
	private $oSimpleXmlElement;
	CONST NEWLINE = "\n";

	/**
	 * @var array $arrEntry : Enthaelt allen Datensaetze
	 * array['id1'](
	 * 		'param'	=> array(
	 * 					'text'  => 'Linktext',
	 * 					'url'   => 'Linkurl',
	 * 					'title' => 'Title',
	 * 					'expand'  => boolean
	 * 					),
	 * 		'child' => array['id2'](...)|NULL
	 * */
	private $arrEntry;


	function __construct ($_xml, $_baseUrl = '', $_name = NULL) {
		$this->oSimpleXmlElement	= simplexml_load_string($_xml);
		$this->basePath 			= $_baseUrl;
		$this->id					= $_name;

		if (!is_object($this->oSimpleXmlElement)) {
			throw new Exception('Unable to load XML');
		}

		$arrEntry = $this->convertToArray($this->oSimpleXmlElement);
		$this->arrEntry = $arrEntry;
//		echo '<pre>'.print_r($arrEntry, true).'</pre>';
	}


	/**
	 * @return array()
	 * */
	private function convertToArray (SimpleXMLElement $_oSimpleXmlElement) {
		$arrResult	= array();
		$list 		= $_oSimpleXmlElement->element; // Alle Hauptkategoriepunkte durchlaufen

		/* @var $val  SimpleXMLElement*/
		foreach ($list as $key => $val) {
			$attr		= $val->attributes();

			$selected	= (isset($attr->selected)) ? $attr->selected->__toString() : '';
			$expand		= (isset($attr->expand)) ? $attr->expand->__toString() : '';

			$arrParam = array(
				'id'		=> $attr->id->__toString(),
				'text'		=> $attr->text->__toString(),
				'title'		=> $attr->title->__toString(),
				'url'		=> $attr->url->__toString(),
				'selected'	=> $selected,
				'expand'	=> $expand
			);

			$hasChildren = count($val->children());
			if ($hasChildren >= 1) {
				$arrChild = $this->convertToArray($val->children());
			} else {
				$arrChild = NULL;
			}

			$arrResult[$attr->id->__toString()] = array('param' => $arrParam, 'child' => $arrChild);
		}

		return $arrResult;
	}


	/**
	* Sucht anhand der Id einen Eintrag und gibt diesen als SimpleXMLElement aus.
	* @param int|string $id : Id des als aktive zu definierenden Eintrags
	* @return array()|NULL
	*/
	public function find ($_id) {
		return $this->findHelper($_id, $this->arrEntry);
	}
	private function findHelper ($_id, array $_arrEntry) {
		if (count($_arrEntry)) {
			foreach ($_arrEntry as $key => $arrParent) {
				//echo $key.'<br/>';
				if ($key == $_id) {
					return $arrParent;
				}

				if (is_array($arrParent['child'])) {
					return $this->findHelper($_id, $arrParent['child']);
				}
			}
		}

		return NULL;
	}

	public function changeParam ($_id, $paramName, $paramValue) {
		$this->arrEntry = $this->changeParamHelper($_id, $this->arrEntry, $paramName, $paramValue);
	}
	private function changeParamHelper ($_id, array &$_arrEntry, $paramName, $paramValue) {
		$arrResult = array();
		$arrTmp = array();

		if (count($_arrEntry)) {
			foreach ($_arrEntry as $key => $arrParent) {
				$arrResult[$key] = $arrParent;
				if ($_id == $key) {
					$arrResult[$key]['param'][$paramName] = $paramValue;
				}
				if (is_array($arrParent['child'])) {
					$arrResult[$key]['child'] = $this->changeParamHelper($_id, $arrParent['child'], $paramName, $paramValue);
				} else {
					$arrResult[$key]['child'] = NULL;
				}
			}
		}

		$_arrEntry = $arrResult;
		return $_arrEntry;
	}


	/**
	* Liefert alle Kindelemente eines Vaters.
	* @param int|string $id: Zu verarbeitende XML-Daten
	* @return Array()|NULL
	*/
	public function getChildren ($_id) {
		return $this->getChildrenHelper($_id, $this->arrEntry);
	}
	private function getChildrenHelper ($_id, array $_arrEntry) {
		if (count($_arrEntry)) {
			foreach ($_arrEntry as $key => $arrParent) {
				//echo $key.'<br/>';
				if ($key == $_id) {
					return $arrParent['child'];
				}

				if (is_array($arrParent['child'])) {
					return $this->getChildrenHelper($_id, $arrParent['child']);
				}
			}
		}

		return NULL;
	}


	/**
	 * Liefert das Elternelement eines Kindes.
	 * @param int|string $id: Zu verarbeitende XML-Daten
	 * @return string|NULL
	 */
	public function getParent ($_id) {
			return $this->getParentHelper($_id, $this->arrEntry);
	}
	private function getParentHelper ($_id, array $_arrEntry) {
		if (count($_arrEntry)) {
			foreach ($_arrEntry as $key => $arrParent) {
				//echo $key.'<br/>';
				if ($key == $_id) {
					return $arrParent;
				}

				if (is_array($arrParent['child'])) {
					if ($this->getParentHelper($_id, $arrParent['child']) != NULL) {
						return $arrParent;
					}
				}
			}
		}

		return NULL;
	}


	/**
	* Definiert den aktiven Menueeintrag.
	* @param int|string $id : Id des als aktive zu definierenden Eintrags
	* @param boolean $_option : Angabe, ob selektiert oder deselektiert werden soll
	* @return bool
	*/
	public function setSelected ($_id) {
		return $this->changeParam($_id, 'selected', true);
	}


	/**
	 * Definiert den aktiven Menueeintrag.
	 * @param int|string $id : Id des als aktive zu definierenden Eintrags
	 * @param boolean $_option : Angabe, ob selektiert oder deselektiert werden soll
	 * @return bool
	 */
	public function setUnselected ($_id) {
		return $this->changeParam($_id, 'selected', '');
	}


	/**
	 * @desc Zeigt alle Kindelemente eines Vaters an.
	 * @param int|string $_id : Id des als aktiv zu definierenden Eintrags
	 * @return bool
	 */
	public function expand ($_id) {
		return $this->changeParam($_id, 'expand', true);
	}


	/**
	 * @desc Blendet alle Kindelemente eines Vaters aus.
	 * @param int|string $_id : Id des als aktiv zu definierenden Eintrags
	 * @return bool
	 */
	public function collapse($_id) {
		return $this->changeParam($_id, 'expand', '');
	}


	/**
	 * @desc Fuegt einem Menue einen neuen Menuepunkt hinzu.
	 * @param int|string $_id : Id des als aktiv zu definierenden Eintrags
	 * @param string $_url (optional) : Url wenn es ein Link sein soll
	 * @param string $_text: Linktext oder sonstigen HTML-Code
	 * @param string $_title (optional) : Titelbeschreibung des Links
	 * @return void
	 */
	public function addBefore ($_idAfter, $_id, $_url, $_text, $_title = '') {
		$arrParam = array(
				'id'		=> $_id,
				'text'		=> $_text,
				'title'		=> $_title,
				'url'		=> $_url,
				'selected'	=> '',
				'expand'	=> ''
		);
		$arr = $this->addHelper($_idAfter, $this->arrEntry, $arrParam, $_option = 'before');
	}


	/**
	* @desc Fuegt einem Menue einen neuen Menuepunkt hinzu.
	* @param int|string $_id : Id des als aktiv zu definierenden Eintrags
	* @param string $_url (optional) : Url wenn es ein Link sein soll
	* @param string $_text: Linktext oder sonstigen HTML-Code
	* @param string $_title (optional) : Titelbeschreibung des Links
	* @return void
	*/
	public function addAfter ($_idAfter, $_id, $_url, $_text, $_title = '') {
		$arrParam = array(
			'id'		=> $_id,
			'text'		=> $_text,
			'title'		=> $_title,
			'url'		=> $_url,
			'selected'	=> '',
			'expand'	=> ''
		);
		$arr = $this->addHelper($_idAfter, $this->arrEntry, $arrParam, $_option = 'after');
	}
	private function addHelper ($_id, array &$_arrEntry, $arrParam, $_option) {
		$i=0;
		$arrResult = array();
		$arrTmp = array();

		if (count($_arrEntry)) {
			foreach ($_arrEntry as $key => $arrParent) {
				$i++;

				if (is_array($arrParent['child'])) {
					$arrResult[$key] = $arrParent;
					$arrResult[$key]['child'] = $this->addHelper($_id, $arrParent['child'], $arrParam,$_option);
				} else {
					if ($_option == 'after') {
						$arrResult[$key] = $arrParent;
					}
					if ($key == $_id) {
						$arrResult[$arrParam['id']] = $arrTmp[$arrParam['id']] = array('param' => $arrParam, 'child' => NULL);
					}
					if ($_option == 'before') {
						$arrResult[$key] = $arrParent;
					}
				}
			}
		}

		$_arrEntry = $arrResult;
		return $_arrEntry;
	}


	/**
	 * @desc Bearbeitet einen vorhandenen Menueeintrag.
	 * @param int|string $_id : Id des als aktiv zu definierenden Eintrags
	 * @param string $_urlNew (optional) : Url wenn es ein Link sein soll
	 * @param string $_textNew: Linktext oder sonstigen HTML-Code
	 * @param string $_titleNew (optional) : Titelbeschreibung des Links
	 * @return bool
	 */
	public function edit ($_id, $_urlNew, $_textNew, $_titleNew = '') {
		$arrParam = array(
			'id'		=> $_id,
			'text'		=> $_textNew,
			'title'		=> $_titleNew,
			'url'		=> $_urlNew
		);
		$arr = $this->editHelper($_id, $this->arrEntry, $arrParam);
	}
	private function editHelper ($_id, array &$_arrEntry, $arrParam) {
		$i=0;
		$arrResult = array();
		$arrTmp = array();

		if (count($_arrEntry)) {
			foreach ($_arrEntry as $key => $arrParent) {
				$i++;

				if (is_array($arrParent['child'])) {
					$arrResult[$key] = $arrParent;
					$arrResult[$key]['child'] = $this->editHelper($_id, $arrParent['child'], $arrParam);
				} else {
					if ($key == $_id) {
						$arrParam['selected']		= $arrParent['param']['selected'];
						$arrParam['expand']			= $arrParent['param']['expand'];
						$arrResult[$arrParam['id']] = $arrTmp[$arrParam['id']] = array('param' => $arrParam, 'child' => NULL);
					} else {
						$arrResult[$key] = $arrParent;
					}
				}
			}
		}

		$_arrEntry = $arrResult;
		return $_arrEntry;
	}


	/**
	 * @desc Entfernt den Menueeintrag aus der Liste.
	 * @param int|string $_id : Id des als aktiv zu definierenden Eintrags
	 * @return bool
	 */
	public function delete ($_id) {
			$arr = $this->deleteHelper($_id, $this->arrEntry);
	}
	private function deleteHelper ($_id, array &$_arrEntry) {
		$i=0;
		$arrResult = array();
		$arrTmp = array();

		if (count($_arrEntry)) {
			foreach ($_arrEntry as $key => $arrParent) {
				$i++;

				if (is_array($arrParent['child']) && $key != $_id) { // Wenn Unterelemente hat und nicht die gesuchte Id ist, mache weiter
					$arrResult[$key] = $arrParent;
					$arrResult[$key]['child'] = $this->deleteHelper($_id, $arrParent['child']);
				} else if ($key != $_id) { // Wenn keine Unterelemente und nicht die gesuchte Id ist, mache weiter
					$arrResult[$key] = $arrParent;
				}
			}
		}

		$_arrEntry = $arrResult;
		return $_arrEntry;
	}


	/**
	 * Gibt das erzeugte Array aus.
	 * @return array
	 */
	public function getList() {
		return $this->arrEntry;
	}


	/**
	* Gibt das fertige Menue aus.
	* @return string
	*/
	public function display ($entryPoint = NULL) {
		if ($entryPoint == NULL) {
			$arrEntries = $this->arrEntry;
		} else {
			$arrEntries = $this->getChildren($entryPoint);
		}
		return '<div id="'.$this->id.'">'.$this->displayHelper($arrEntries).'</div>';
	}
	private function displayHelper ($arrEntry) {
		$result = '';

		/* @var $val SimpleXmlElement */
		$result .= self::NEWLINE.'<ul>'.self::NEWLINE;
		if (count($arrEntry)) {
			foreach ($arrEntry as $key => $val) {
				$attr		= $val['param'];

				$text		= $attr['text'];
				$title		= $attr['title'];
				$url		= $attr['url'];
				$id			= $attr['id'];
				$selected	= ($attr['selected'] == 1) ? ' class="selected"' : '';
				$expand		= ($attr['expand'] == 1) ? $attr['expand'] : false;

				$result .= '<li><a id="'.$id.'" href="'.$this->basePath.$url.'" title="'.$title.'"'.$selected.'>'.$text.'</a></li>'.self::NEWLINE;

				if ($expand == 1) {
					$hasChildren = count($val['child']);
					if ($hasChildren >= 1) {
						$child = $this->displayHelper($val['child']);
						$result .= $child.self::NEWLINE;
					}
				}
			}
		}
		$result .= '</ul>'.self::NEWLINE;

		return $result;
	}


}


?>